<template>
  <div class="comment-input-comp" :class="{ focused: showAction }" @click="commentInputClick" v-clickoutside="handleClickOutside">
    <div class="avatar-box" v-if="showAvatar">
      <a-avatar class="avatar-icon" slot="avatar" :src="avatarUrl" icon="user" />
    </div>
    <div class="form-box">
      <div class="input-box">
        <div ref="replyInputRef"
             class="rich-input"
             contenteditable="true"
             spellcheck="false"
             v-html="editText"
             :disabled="!editable"
             :placeholder="placeholder"
             @focus="handleFocus"
             @input="handleInput"></div>
      </div>
  
      <div class="actions-box" v-if="showAction">
        <div class="submit">
          <a-button :loading="submitting" type="primary" :disabled="!editText" @click="handleSubmit">评论</a-button>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
  import { Component, Vue, Prop, Model, Emit, Watch } from 'vue-property-decorator';
  
  interface IDataSource {
    articleId: string;
    content: string;
    replyUserId: string;
    replyCommentId: string;
  }
  
  @Component
  export default class CommentInput extends Vue {
    @Model('change', { type: String, required: false, default: '' })
    readonly value?: string;
    
    @Prop({ type: Object, required: true, default: {} })
    readonly dataSource!: IDataSource;
    
    @Prop({ type: Boolean, required: false, default: false })
    readonly showAvatar?: boolean;
    
    @Prop({ type: String, required: false, default: '' })
    readonly avatarUrl?: string | undefined;
    
    @Prop({ type: String, required: false, default: '输入评论...' })
    readonly placeholder?: string;
    
    @Prop({ type: Boolean, required: false, default: true })
    readonly editable?: boolean;
    
    @Prop({ type: Boolean, required: false, default: false })
    readonly autofocus?: boolean;
    
    @Emit('change')
    onChange(val: string): void {}
    
    @Emit('progress')
    onProgress(eventType: 'outside' | 'save' | 'click'): void {}
    
    @Emit('save')
    onSave(): void {}
    
    @Watch('value')
    onWatchValue(newVal: string): void {
      this.editText = newVal;
    }
    
    showAction: boolean = false;
    editText: string = '';
    submitting: boolean = false;
    
    private isLock: boolean = false;
    private el!: HTMLElement;
    
    private onCompositionstart(): void {
      this.isLock = true;
    }
    
    private onCompositionend(): void {
      this.isLock = false;
    }
  
    // 解决光标定位问题
    private getCursor(obj: any): void {
      if (window.getSelection) { //ie11 10 9 ff safari
        obj.focus(); // 解决ff不获取焦点无法定位问题
        const range: any = window.getSelection(); //创建range
        range.selectAllChildren(obj); //range 选择obj下所有子内容
        range.collapseToEnd(); //光标移至最后
      } else if ((document as any).selection) { // ie10 9 8 7 6 5
        const range = (document as any).selection.createRange(); //创建选择对象
        range.moveToElementText(obj); //range定位到obj
        range.collapse(false); //光标移至最后
        range.select();
      }
    }
    
    private created(): void {
      this.editText = this.value || '';
      if (this.editable) {
        this.showAction = !!this.autofocus;
      }
    }
    
    private mounted(): void {
      // 监听中文输入
      this.el = this.$refs['replyInputRef'] as HTMLElement;
      this.el.addEventListener('compositionstart', this.onCompositionstart, false);
      this.el.addEventListener('compositionend', this.onCompositionend, false);
      
      if (this.autofocus && this.el) {
        this.el.focus();
      }
    }
    
    private beforeDestroy(): void {
      // 清除监听中文输入
      this.el.removeEventListener('compositionstart', this.onCompositionstart, false);
      this.el.removeEventListener('compositionend', this.onCompositionend, false);
    }
  
    handleClickOutside(): void {
      if (!this.editable) return;
      
      if (this.editText.trim() === '') {
        this.showAction = false;
        this.onProgress('outside');
      }
    }
  
    commentInputClick(): void {
      if (!this.editable) {
        this.onProgress('click');
        return;
      }
      
      if (this.el) {
        this.el.focus();
      }
    }
  
    handleFocus(e: any): void {
      if (!this.editable) {
        this.el.blur();
      } else {
        this.showAction = true;
        this.getCursor(e.target);
      }
    }
  
    /**
     * 评论输入回调
     */
    handleInput(e: any): void {
      // 解决中文输入的时候，直接输出英文字母的问题(中文输入期间，不允许输入字符)
      setTimeout(() => {
        if (this.isLock) return;
        let value = e.target.innerHTML;
        // 去除换行符
        value = value.replace(/(\<\/?\w*\>)/ig, '')
          .replace(/(\<div\>\<br\>\<\/div\>)/ig, '')
          .replace(/(\r\n)|(\n)/g, '');
  
        this.editText = value.trim();
        this.onChange(value);
      }, 0);
  
      // 如果是pc的话，timeout设置为5就好，ios经测试，使用40才有效果
      const timeout = 5;
      setTimeout(() => {
        this.getCursor(e.target);
      }, timeout);
    }
  
    /**
     * 提交评论
     */
    handleSubmit(e: any): void {
      e.stopPropagation();
      if (!this.editText) {
        return;
      }
      this.submitting = true;
      const data = {
        articleId: this.dataSource.articleId,
        content: this.editText,
        to: this.dataSource.replyUserId || null,
        replyCommentId: this.dataSource.replyCommentId || null,
      };
      this.$store.dispatch('comment/addComment', data).then(() => {
        this.submitting = false;
        this.showAction = false;
        this.editText = '';
        this.onChange(this.editText);
        this.onProgress('save');
        this.onSave();
      });
    }
  }
</script>

<style lang="scss" scoped>
@import "../assets/scss/design/colors";

.comment-input-comp {
  display: flex;
  position: relative;
  padding: 12px;
  background-color: $color-border-light;
  border-radius: 4px;
  
  .avatar-box {
    flex: 0 0 auto;
    
    .avatar-icon {
      margin: 0 12px 0 0;
      width: 32px;
      height: 32px;
    }
  }
  
  .form-box {
    flex: 1 1 auto;
    position: relative;
  
    .input-box {
      font-size: 0;
      background-color: #fff;
      border: 1px solid #f1f1f1;
      border-radius: 3px;
    
      .rich-input {
        position: relative;
        padding: 6px 12px;
        font-size: 14px;
        line-height: 1.5;
        color: #17181a;
        outline: none;
        min-height: 34px;
      
        &:empty:before {
          content: attr(placeholder);
          position: absolute;
          opacity: .4;
          pointer-events: none;
          user-select: none;
        }
      }
    }
  
    .actions-box {
      display: flex;
      align-items: center;
      margin: 12px 0 0;
    
      .submit {
        flex: 0 0 auto;
        margin-left: auto;
      }
    }
  }
  
  &.focused {
    .input-box {
      border-color: #007fff;
    }
  }
}
</style>
